/**
 * bundle管理器
 */
import { DEBUG } from "cc/env";
import { EntryDelegate } from "../entry/EntryDelegate";
import { Macro } from "../Main/Macros";
import c3d from "../tool/cc";
import { HotUpdate } from "./hotupdate/Hotupdate";



export class BundleManager {
   /**当前游戏子包名字 */
   private curBundle: HotUpdate.BundleConfig = null!;
   /**是否正在加载中 */
   private isLoading = false;
   /**
    * 是否是引擎bundle
    * @param key 包名
    * @returns 
    */
   protected isEngineBundle(key: string) {
      if (key == c3d.AssetManager.BuiltinBundleName.MAIN ||
         key == c3d.AssetManager.BuiltinBundleName.RESOURCES ||
         key == c3d.AssetManager.BuiltinBundleName.START_SCENE) {
         return true;
      }
      return false;
   }
   /**
    * 删除已经加载的bundle （不删除引擎内置包）
    * @param delegate 代理
    * @param excludeBundles 排除的包
    */
   removeLoadedBundle(delegate: EntryDelegate, excludeBundles?: string[]) {
      excludeBundles = excludeBundles ?? delegate.getPersistBundle();
      //筛选所有已经加载的包，引擎内置包不能删除
      let loaded: string[] = [];
      c3d.assetManager.bundles.forEach((bundle, key) => {
         if (!this.isEngineBundle(key)) {
            loaded.push(key);
         }
      });
      //卸载删除
      let i = loaded.length;
      while (i--) {
         let bundle = loaded[i];
         if (excludeBundles.indexOf(bundle) == -1) {
            //在排除bundle中找不到，直接删除
            GM.entryMgr.onUnloadBundle(bundle);
            let result = this.getBundle(bundle);
            if (result) {
               GM.assetMgr.cache.removeBundle(bundle);
               c3d.assetManager.removeBundle(result);
            }
         }
      }
   }

   getBundle(bundle: BUNDLE_TYPE) {
      if (bundle) {
         if (typeof bundle == "string") {
            return c3d.assetManager.getBundle(bundle);
         }
         return bundle;
      }
   }

   getBundleName(bundle: BUNDLE_TYPE): string {
      if (bundle) {
         if (typeof bundle == "string") {
            return bundle;
         } else {
            return bundle.name;
         }
      }
      c3d.Out.warning(`输入参数错误 : ${bundle}`);
      return Macro.UNKNOWN;
   }

   /**
    * 外部接口 进入Bundle
    * @param config 配置
    * @param delegate 代理
    */
   enterBundle(config: HotUpdate.BundleConfig, delegate: EntryDelegate) {
      if (this.isLoading) {
         delegate && delegate.onBundleLoading(config);
         return c3d.Out.trace("正在更新游戏，请稍等");
      }
      this.curBundle = config;
      this.isLoading = true;
      GM.assetMgr.hotupdate.bundlesConfig[this.curBundle.bundle] = config;

      //检查更新
      let versionInfo = GM.assetMgr.hotupdate.bundlesConfig[this.curBundle.bundle];
      this.checkUpdate(versionInfo, delegate);
   }

   /**
    * 检测子游戏更新
    * @param versionInfo 版本信息
    * @param delegate 代理
    */
   private checkUpdate(versionInfo: HotUpdate.BundleConfig, delegate: EntryDelegate) {
      let self = this;
      let bundle = this.curBundle.bundle != Macro.BUNDLE_RESOURCES ? this.curBundle.bundle : undefined;
      //移除监听
      GM.eventMgr.ins.off(HotUpdate.Event.HOTUPDATE_DOWNLOAD, this.onDownload.bind(this, delegate), this);
      c3d.Out.trace(`检测更新信息:${versionInfo.name}(${versionInfo.bundle})`);
      GM.assetMgr.hotupdate.checkUpdate((code, state) => {
         if (code == HotUpdate.Code.NEW_VERSION_FOUND) {
            GM.eventMgr.ins.on(HotUpdate.Event.HOTUPDATE_DOWNLOAD, this.onDownload.bind(this, delegate), this);
            c3d.Out.trace(`检测到${versionInfo.name}(${versionInfo.bundle})有新的版本`);
            delegate && delegate.onNewVersionFund(versionInfo, code, state);
         } else if (state == HotUpdate.State.TRY_DOWNLOAD_FAILED_ASSETS) {
            GM.eventMgr.ins.on(HotUpdate.Event.HOTUPDATE_DOWNLOAD, this.onDownload.bind(this, delegate), this);
            c3d.Out.trace(`正在尝试重新下载之前下载失败的资源`);
            delegate && delegate.onDownloadFailed(versionInfo, code, state);
         } else if (code == HotUpdate.Code.ALREADY_UP_TO_DATE) {
            this.isLoading = false;
            c3d.Out.trace(`已经是最新版本`);
            delegate && delegate.onAreadyUpToData(versionInfo, code, state);
         } else if (code == HotUpdate.Code.ERROR_DOWNLOAD_MANIFEST || code == HotUpdate.Code.ERROR_NO_LOCAL_MANIFEST || code == HotUpdate.Code.ERROR_PARSE_MANIFEST) {
            this.isLoading = false;
            c3d.Out.trace(`下载/解析manifest文件失败`);
            delegate && delegate.onDownloadManifestFailed(versionInfo, code, state);
         } else if (code == HotUpdate.Code.CHECKING) {
            c3d.Out.trace(`正在检测更新!!`);
            delegate && delegate.onCheckingVersion(versionInfo, code, state);
         } else {
            this.isLoading = false;
            c3d.Out.trace(`检测更新当前状态 code : ${code} state : ${state}`);
            delegate && delegate.onOtherReason(versionInfo, code, state);
         }
      }, bundle);
   }

   /**
    * 加载bundle包
    * @param delegate 代理
    */
   loadBundle(delegate?: EntryDelegate) {
      let bundle = this.getBundle(this.curBundle.bundle);
      let versionInfo = GM.assetMgr.hotupdate.bundlesConfig[this.curBundle.bundle];
      if (bundle) {
         this.isLoading = false;
         c3d.Out.trace(`${this.curBundle.bundle}已经加载在缓存中，直接使用`);
         delegate && delegate.onLoadBundleComplete(versionInfo, bundle);
      } else {
         this.isLoading = true;
         c3d.Out.trace(`loadBundle : ${this.curBundle.bundle}`);
         c3d.assetManager.loadBundle(versionInfo.bundle, (err, bundle) => {
            this.isLoading = false;
            if (err) {
               c3d.Out.warning(`load bundle : ${versionInfo.bundle} fail !!!`);
               delegate && delegate.onLoadBundleError(versionInfo, err);
            } else {
               c3d.Out.trace(`load bundle : ${versionInfo.bundle} success !!!`);
               delegate && delegate.onLoadBundleComplete(versionInfo, bundle);
            }
         });
      }
   }
   /**
    * 准备进入下载资源状态时
    * @param delegate 代理
    * @param info 下载信息
    */
   private onDownload(delegate: EntryDelegate, info: HotUpdate.DownLoadInfo) {
      DEBUG && c3d.Out.trace(JSON.stringify(info));
      let config = GM.assetMgr.hotupdate.getBundleName(this.curBundle.bundle);
      if (info.code == HotUpdate.Code.UPDATE_FINISHED) {
         c3d.Out.trace(`更新${config.name}成功`);
      } else if (info.code == HotUpdate.Code.UPDATE_FAILED ||
         info.code == HotUpdate.Code.ERROR_NO_LOCAL_MANIFEST ||
         info.code == HotUpdate.Code.ERROR_DOWNLOAD_MANIFEST ||
         info.code == HotUpdate.Code.ERROR_PARSE_MANIFEST ||
         info.code == HotUpdate.Code.ERROR_DECOMPRESS) {
         this.isLoading = false;
         c3d.Out.warning(`更新${config.name}失败`);
      }
      delegate && delegate.onDownloading(this.curBundle, info);
   }

   /**
    * 打印bundle管理器状态信息
    * @param delegate 
    */
   print(delegate: ManagerPrintDelegate<{
      loaded: c3d.AssetManager.Bundle[], //已在加载的bundle
      curBundle: HotUpdate.BundleConfig, //当前运行的bundle
      areadyLoaded: { [key: string]: HotUpdate.BundleConfig } //已经加载过的bundle配置信息
      isLoading: boolean //是否存在加载bundle过程中
   }>) {
      if (delegate) {
         let loaded: c3d.AssetManager.Bundle[] = [];
         c3d.assetManager.bundles.forEach((bundle, key) => {
            loaded.push(bundle);
         });
         delegate.print({
            loaded: loaded,
            curBundle: this.curBundle,
            areadyLoaded: GM.assetMgr.hotupdate.bundlesConfig,
            isLoading: this.isLoading
         })
      }
   }
}
